/*
 * @Description: 注释内容----&gt;
 * @Author: zlz
 * @GitHub: https://github.com/Arc-zlz/study
 * @LastEditors: HI! 老兄你改了代码写一下注释
 * @Date: 2019-03-23 13:52:59
 * @LastEditTime: 2019-03-23 16:18:29
 */
function Point(x, y) {
  this.x = x;
  this.y = y;
}
// Point在原型链上定义了一个 toString() 函数
Point.prototype.toString = function () {
  return `(${this.x},${this.y})`
}
// p 是 Point 一个实例 继承 Point 原型链上的方法在 __proto__
// var p = new Point(1, 2);
// console.dir(Point.prototype.constructor)
// console.dir(p.toString());
//  上面的这种写法是es5的写法

//  es6 通过'类'转换语法糖来改写原型链的方式  但是这个类的核心还是原型链之间的关系

class Point_class {
  // 通过 constructor (构造器)来定义构造函数
  constructor(x, y) {
    this.x = x;
    this.y = y;
    // return `(${this.x}+${this.y})`
  }
  // 在 Point_class 的原型链上定义一个方法
  toString() {
    return this.x + this.y
  }
}
// console.dir(Point_class.prototype.constructor === Point_class)//true

// 使用类可以直接new 一个类创建一个新的实例
// console.dir(Point_class)

/**
 * __proto__ 并不是语言本身的特性，这是各大厂商具体实现时添加的私有属性，虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性，但依旧不建议在生产中使用该属性，避免对环境产生依赖。生产环境中，我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型，然后再来为原型添加方法/属性。
 */
let o = new Point_class(2225, 1511);
let oo = new Point_class(1, 2);
// console.dir(o.__proto__ === oo.__proto__)//true

//  class 表达式
//这样定义一个类是一个立即执行的类
let person = new class {
  constructor(name) {
    this.name = name;
  }
  sayGetName() {
    console.log(this.name);
  }
}("章撒喊");
// person.sayGetName();//章撒喊


// 私有属性和私有方法
// 以前使用私有属性是通过_变量名来区分私有属性 但是这种方法并不保险 因为这种方法外面也可以调用
// class Widget {
//   // 公有方法
//   foo(baz) {
//     this._bar(baz);
//   }
//   // 私有方法
//   _bar(baz) {
//     return this.sfs = baz;
//   }
// }

/**
 * 上面代码中，_bar方法前面的下划线，表示这是一个只限于内部使用的私有方法。但是，这种命名是不保险的，在类的外部，还是可以调用到这个方法。
 * 另一种方法就是索性将私有方法移出模块，因为模块内部的所有方法都是对外可见的。
 */
// class Widget {
//   foo(baz) {
//     bar.call(this, baz);
//   }

//   // ...
// }

// function bar(baz) {
//   return this.snaf = baz;
// }

// 上面代码中，foo是公有方法，内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法。
// 还有一种方法是利用Symbol值的唯一性，将私有方法的名字命名为一个Symbol值。

// const bar = Symbol('bar');
// const sfs = symbol('sfs');

// export default class myClass{

//   // 公有方法
//   foo(baz) {
//     this[bar](baz);
//   }

//   // 私有方法
//   [bar](baz) {
//     return this[sfs] = baz;
//   }

//   // ...
// };

// 上面的方法还是没有完全的解决这个问题
// es6 有一个天提案就是将#放在属性名的前面 这样就变成了class私有属性
// class Point {
//   #x;

//   constructor(x = 0) {
//     #x = +x; // 写成 this.#x 亦可
//   }

//   get x() { return #x }
//   set x(value) { #x = +value }
// }
//支持不好 不建议使用

// 现在最好的解决办法就是 TS 中 class的关键字

// Class中的取值函数(getter)和存值函数(setter)

class MyClass {
  constructor() {

  }
  get port() {
    return console.log('我拿到了这个值是默认值');
  }
  /**
   * @param {any} value
   */
  set port(value) {
    console.log(`我设置了value---->${value}`);
  }
}
let install = new MyClass();
// install.port;//我拿到了这个值是默认值
// install.port = 123;//我设置了value----123

// 上面代码中，prop属性有对应的存值函数和取值函数，因此赋值和读取行为都被自定义了。
// 存值函数和取值函数是设置在属性的 Descriptor 对象上的。

class CustomHTMLElement {
  constructor(element) {
    this.element = element;
  }
  get html() {
    return this.element.innerHtml;
  }
  set html(value) {
    this.element.innerHtml = value;
  }
}


let descriptor = Object.getOwnPropertyDescriptor(
  CustomHTMLElement.prototype, "html"
)
//Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。（自有属性指的是直接赋予该对象的属性，不需要从原型链上进行查找的属性）
// 一个属性描述符是一个记录，由下面属性当中的某些组成的：
// value
// 该属性的值(仅针对数据属性描述符有效)
// writable
// 当且仅当属性的值可以被改变时为true。(仅针对数据属性描述有效)
// get
// 获取该属性的访问器函数（getter）。如果没有访问器， 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)
// set
// 获取该属性的设置器函数（setter）。 如果没有设置器， 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)
// configurable
// 当且仅当指定对象的属性描述可以被改变或者属性可被删除时，为true。
// enumerable
// 当且仅当指定对象的属性可以被枚举出时，为 true。
// console.log("get" in descriptor);//true
// console.log("set" in descriptor);//true

//  Class 的静态方法

class Foo {
  static classMethod() {
    return "Hello"
  }
}
// console.log(Foo.classMethod());//Hello
let FooFo = new Foo();
// console.log(FooFo.classMethod());//报错

// class 的静态属性和实例属性

// 静态属性指的是 Class 本身的属性，即Class.propName，而不是定义在实例对象（this）上的属性。
// 支持度不好

// 1 类的实例属性
// class static_attr{
//   myProp = 42;
//   constructor(){
//     console.log(this.myProp);
//   }
// }

// let ya = new static_attr();
// ya
